package ejb.contentManagement;


import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;

import org.joda.time.DateTime;

import java.util.List;

import javax.annotation.security.RolesAllowed;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.apache.commons.io.IOUtils;

import ejb.dto.ExcursionDTO;
import ejb.dto.FlightDTO;
import ejb.dto.HotelDTO;
import ejb.dto.PackageDTO;
import ejb.reservationManagement.PackageMgr;
import entities.Flight;
import entities.Location;
import entities.Excursion;
import entities.Hotel;
import entities.HotelAvailability;
import entities.Package;
import entities.Period;

@Stateless
public class PackageBean implements PackageMgr {
	
	@PersistenceContext
    private EntityManager em;
	
	/**
	 * Retrieve entity Package from the associated Id
	 * @param id
	 * @return entity Package associated to <code>id</code>
	 */
	public Package getPackage(int id) {
		return em.find(Package.class, id);
	}
	
	/**
	 * Retrieve all Package entities
	 * @return all Package entities
	 */
	public List<Package> getAllPackages() {
    	return em.createNamedQuery("Package.findAll", Package.class).getResultList();
    }
	
	/**
	 * Retrieve entity Package from the associated Id and convert it to corresponding PackageDTO
	 */
	@Override
	public PackageDTO getPackageDTO(int id) {
		return  convertToDTO(getPackage(id));
    }
	
	/**
	 * Retrieve all Package entities and convert them into corresponding list of PackageDTO
	 */
	@Override
	public List<PackageDTO> getAllPackagesDTO() {
		List<Package> list = getAllPackages();
		List<PackageDTO> newList = new ArrayList<PackageDTO>();
		for(Package obj : list) {
			newList.add(convertToDTO(obj));
		}
		return newList;
	}
	
	/**
	 * Convert Package entity (parameter <code>p</code>) into corresponding PackageDTO
	 * @param p
	 * @return converted PackageDTO
	 */
	public static PackageDTO convertToDTO(entities.Package p) {
		if(p == null) {
			return null;
		}
		PackageDTO dto = new PackageDTO();
		dto.setId(p.getId());
		dto.setName(p.getName());
		dto.setDescription(p.getDescription());
		dto.setLocation(LocationBean.convertToDTO(p.getLocation()));
		dto.setPeriod(PeriodBean.convertToDTO(p.getPeriod()));
		dto.setHotel(HotelBean.convertToDTO(p.getHotel()));
		dto.setExcursion(ExcursionBean.convertToDTO(p.getExcursion()));
		
		float minPrice = minimumPrice(p);
		dto.setMinPrice(minPrice);
		if(minPrice > 0) {
			dto.setExtra("A partire da " + minPrice + "euro");
		}
		else {
			dto.setExtra("Pacchetto viaggio attualmente non disponibile");
		}
		
		List<HotelDTO> hotels = new ArrayList<HotelDTO>();
		for(Hotel h : p.getHotels()) {
			hotels.add(HotelBean.convertToDTO(h));
		}
		dto.setHotels(hotels);
		
		List<ExcursionDTO> excursions = new ArrayList<ExcursionDTO>();
		for(Excursion e : p.getExcursions()) {
			excursions.add(ExcursionBean.convertToDTO(e));
		}
		dto.setExcursions(excursions);
		
		List<FlightDTO> flights1 = new ArrayList<FlightDTO>();
		for(Flight f : p.getFlights1()) {
			flights1.add(FlightBean.convertToDTO(f));
		}
		dto.setFlights1(flights1);
		
		List<FlightDTO> flights2 = new ArrayList<FlightDTO>();
		for(Flight f : p.getFlights2()) {
			flights2.add(FlightBean.convertToDTO(f));
		}
		dto.setFlights2(flights2);
		
		return dto;
	}
	
	/**
	 * Retrieve the overall package minimum price 
	 * @param p
	 * @return the overall package minimum price
	 */
	private static float minimumPrice(entities.Package p) {
		//Loose period: accepted until +-1 day on default period
		Date start = new DateTime(p.getPeriod().getFromDate()).minusDays(1).toDate();
		Date end = new DateTime(p.getPeriod().getToDate()).plusDays(1).toDate();
		
		float hotelPrice = 0;
		for(Hotel h : p.getAllHotels()) {
			for(HotelAvailability ha : h.getHotelAvailabilities()) {
				if((hotelPrice == 0 || (ha.getPrice() * ha.getDays())<hotelPrice) && (ha.getRemaining()>0 && ha.getFromDate().after(start) && ha.getToDate().before(end))) {
					hotelPrice = ha.getDays() * ha.getPrice();
				}
			}
		}
		float excursionPrice = 0;
		for(Excursion e : p.getAllExcursions()) {
			if(excursionPrice == 0 || e.getPrice()<excursionPrice) {
				excursionPrice = e.getPrice();
			}
		}
		float flightsPrice1 = 0;
		for(Flight f : p.getFlights1()) {
			if((flightsPrice1 == 0 || f.getPrice()<flightsPrice1) && (f.getRemaining()>0 && f.getDepartureTime().after(start) && f.getLandingTime().before(end))) {
				flightsPrice1 = f.getPrice();
			}
		}
		float flightsPrice2 = 0;
		for(Flight f : p.getFlights2()) {
			if((flightsPrice2 == 0 || f.getPrice()<flightsPrice2) && (f.getRemaining()>0 && f.getDepartureTime().after(start) && f.getLandingTime().before(end))) {
				flightsPrice2 = f.getPrice();
			}
		}
		if(hotelPrice == 0 || excursionPrice == 0 || flightsPrice1 == 0 || flightsPrice2 == 0) {
			return 0;
		}
		return hotelPrice + excursionPrice + flightsPrice1 + flightsPrice2;
	}
	
	/**
	 * Retrieve entity Package from the associated Id and delete the corresponding element from the database
	 */
	@Override
	@RolesAllowed({"ADMINS"})
	public boolean deletePackage(int id) {
		try{
			em.remove(em.find(Package.class, id));
			em.flush();
		}
		//Catch exceptions in the delete operation from database
		catch(Exception e){
			return false;
		}
		return true;
	}
	
	/**
	 * Create entity Package from corresponding PackageDTO and update corresponding element in database
	 */
	@Override
	@RolesAllowed({"ADMINS"})
	public boolean updatePackage(PackageDTO updatedPackage) {
		if(updatedPackage == null ||
				updatedPackage.getName() == null ||
				updatedPackage.getName() == "" ||
				updatedPackage.getImage() == null ||
				updatedPackage.getDescription() == null ||
				updatedPackage.getDescription() == "" ||
				updatedPackage.getInwardFlightIds() == null ||
				updatedPackage.getOutwardFlightIds() == null ||
				updatedPackage.getInwardFlightIds().size() == 0 ||
				updatedPackage.getOutwardFlightIds().size() == 0
				) {
			return false;
		}

		List<Flight> flightList1 = new ArrayList<Flight>();
		List<Flight> flightList2 = new ArrayList<Flight>();
		
		List<Hotel> hotelList = new ArrayList<Hotel>();
		List<Excursion> excursionList = new ArrayList<Excursion>();
		
		for(int flightId : updatedPackage.getOutwardFlightIds()) {
			flightList1.add(em.find(Flight.class, flightId));
		}
		for(int flightId : updatedPackage.getInwardFlightIds()) {
			flightList2.add(em.find(Flight.class, flightId));
		}
		for(int hotelId : updatedPackage.getHotelIds()) {
			hotelList.add(em.find(Hotel.class, hotelId));
		}
		
		for(int excursionId : updatedPackage.getExcursionIds()) {
			excursionList.add(em.find(Excursion.class, excursionId));
		}
		
		entities.Package p = new Package(updatedPackage, em.find(Location.class, updatedPackage.getLocationId()), em.find(Period.class, updatedPackage.getPeriodId()), em.find(Hotel.class, updatedPackage.getHotelId()), em.find(Excursion.class, updatedPackage.getExcursionId()), flightList1, flightList2, hotelList, excursionList);
		try{
			em.merge(p);
			em.flush();
		}
		//Catch exceptions in the update operation of database
		catch(Exception e){
			return false;
		}
		try {
			InputStream input = updatedPackage.getImage().getInputstream();
			File dest = new File("../eclipseApps/TravelDreamEAR/TravelDreamWeb_war/images/packages/" + p.getId() + ".jpg");
			OutputStream output = new FileOutputStream(dest);
			IOUtils.copy(input, output);
			IOUtils.closeQuietly(output);
		    IOUtils.closeQuietly(input);
		}
		//Catch exceptions in the image uploading oparation
		catch(Exception e) {
			e.printStackTrace();
		}
		return true;
		
	}
	
	/**
	 * Create entity Package from corresponding PackageDTO and insert new corresponding element in database
	 */
	@Override
	@RolesAllowed({"ADMINS"})
	public boolean insertPackage(PackageDTO newPackage) {
		
		if(newPackage == null ||
				newPackage.getName() == null ||
				newPackage.getName() == "" ||
				newPackage.getImage() == null ||
				newPackage.getDescription() == null ||
				newPackage.getDescription() == "" ||
				newPackage.getInwardFlightIds() == null ||
				newPackage.getOutwardFlightIds() == null ||
				newPackage.getInwardFlightIds().size() == 0 ||
				newPackage.getOutwardFlightIds().size() == 0
				) {
			return false;
		}

		List<Flight> flightList1 = new ArrayList<Flight>();
		List<Flight> flightList2 = new ArrayList<Flight>();
		
		List<Hotel> hotelList = new ArrayList<Hotel>();
		List<Excursion> excursionList = new ArrayList<Excursion>();
		
		for(int flightId : newPackage.getOutwardFlightIds()) {
			flightList1.add(em.find(Flight.class, flightId));
		}
		for(int flightId : newPackage.getInwardFlightIds()) {
			flightList2.add(em.find(Flight.class, flightId));
		}
		for(int hotelId : newPackage.getHotelIds()) {
			hotelList.add(em.find(Hotel.class, hotelId));
		}
		
		for(int excursionId : newPackage.getExcursionIds()) {
			excursionList.add(em.find(Excursion.class, excursionId));
		}
		
		entities.Package p = new Package(newPackage, em.find(Location.class, newPackage.getLocationId()), em.find(Period.class, newPackage.getPeriodId()), em.find(Hotel.class, newPackage.getHotelId()), em.find(Excursion.class, newPackage.getExcursionId()), flightList1, flightList2, hotelList, excursionList);
		
		try{
			em.persist(p);
			em.flush();
		}
		//Catch exceptions in the insert operation in database
		catch(Exception e) {
			return false;
		}
		try {
			InputStream input = newPackage.getImage().getInputstream();
			File dest = new File("../eclipseApps/TravelDreamEAR/TravelDreamWeb_war/images/packages/" + p.getId() + ".jpg");
			OutputStream output = new FileOutputStream(dest);
			IOUtils.copy(input, output);
			IOUtils.closeQuietly(output);
		    IOUtils.closeQuietly(input);
		}
		//Catch exceptions in the image uploading operation
		catch(Exception e) {
			e.printStackTrace();
		}
		return true;
	}
			

}
